/*
 *      Author: Guannan Ma
 *      @mythmgn
 *      Whatever license applied
 */


#include <stddef.h>
#include <stdio.h>
#include <string.h>

#include "base.h"

/*
 * the closest greater prime numbers when doubling the previous ones.
 */

static unsigned int hTabSize[] = {
    17,             /* 0 */
    37,             /* 1 */
    79,             /* 2 */
    163,            /* 3 */
    331,            /* 4 */
    673,            /* 5 */
    1361,           /* 6 */
    2729,           /* 7 */
    5471,           /* 8 */
    10949,          /* 9 */
    21911,          /* 10 */
    43853,          /* 11 */
    87719,          /* 12 */
    175447,         /* 13 */
    350899,         /* 14 */
    701819,         /* 15 */
    1403641,        /* 16 */
    2807303,        /* 17 */
    5614657,        /* 18 */
    11229331,       /* 19 */
    22458671,       /* 20 */
    44917381,       /* 21 */
    89834777,       /* 22 */
    179669557,      /* 23 */
    359339171,      /* 24 */
    718678369,      /* 25 */
    1437356741,     /* 26 */
    2147483647      /* 27 (largest signed int prime) */
};

#define HTAB_SIZE_NUMS 27

static unsigned int getRealSize(unsigned int size) {
    int i;
    for (i=0; i<=HTAB_SIZE_NUMS; i++) {
        if (size<hTabSize[i])
            return hTabSize[i];
    }
    return hTabSize[0];

}
static int getAddr(hTab *h, const char *key) {
    int hk=0;
    while (*key) {
        hk=(hk*128+(*key++))%(h->size);
    }
    return hk;

}

hTab *
initHashTable(int initSize) {
    hTab *ph;
    int i;
    if(NULL==(ph=(hTab *)malloc(sizeof(hTab)))) {
		setErrStatusCode(MEMORY_MALLOC_FAILURE);
        return NULL;
    }
    ph->size=getRealSize(initSize);
    if (NULL==(ph->hiList=(hItem*)malloc(sizeof(hItem)*ph->size))) {
		setErrStatusCode(MEMORY_MALLOC_FAILURE);
        return 0;
    }
    for (i=0; i<ph->size; i++) {
        ph->hiList[i].next=NULL;
        ph->hiList[i].key=NULL;
        ph->hiList[i].content=NULL;
    }
    return ph;
}

int putHashItem(hTab *h,char *key,void *content) {
    /*If the hItem *hi is already in hTab *h, the insertion will fail. */
	int addr;
    if (NULL!=getHashItem(h,key))
        return 0;
    addr=getAddr(h,key);
    if (addr<0) {
        return 0;
    } else {
        hItem* temp;
        hItem* newNode;
        temp=&(h->hiList[addr]);
        /* If it's the header of the hItem List*/
        while (NULL!=temp->next) {
            temp=temp->next;
        }

        newNode=(hItem* )malloc(sizeof(hItem));
        newNode->key=(char *)malloc(sizeof(char)*MAX_LENGTH_OF_KEY);
        strcpy(newNode->key,key);
        newNode->key[strlen(key)]='\0';
        newNode->content=content;
        newNode->next=NULL;
        temp->next=newNode;
    }
    return 1;

}


void* getHashItem(hTab *h,const char *key) {
    hItem *hi;
    hItem *temp;

    if(NULL==h||0==h->size) {
        return NULL;
    }
    if (NULL==(hi=&(h->hiList)[getAddr(h,key)]))
        return NULL;

    temp=hi->next;
    while (NULL!=temp) {
        if (strcmp(temp->key,key)==0) {
            return temp->content;
        }
        temp=temp->next;
    }
    return NULL;

}

/* Return the pointer of the content */
void* delHashItem(hTab *h,char *key) {
    hItem *hi,*temp,*p;
    if (NULL==(hi=&(h->hiList[getAddr(h,key)])))
        return 0;
    temp=hi->next;
    p=hi;
    while (NULL!=temp) {
        if (strcmp(temp->key,key)==0) {
            void *cr;
            cr=temp->content;
            p->next=temp->next;
            FREEUP(temp->key);
            FREEUP(temp);
            return cr;
        }
        temp=temp->next;
    }

    return NULL;

}

BOOL freeHashTable(hTab *h,BOOL delContent,void  (*handleContentFunc)(void *content)) {
    if (NULL!=h) {
        int i;
        hItem *hi,*temp;
        hi=NULL;
        temp=NULL;
        for (i=0; i<h->size; i++) {
            hi=&(h->hiList[i]);
            while (NULL!=hi) {
                temp=(hi->next!=NULL?hi->next:NULL);
                if ((NULL!=handleContentFunc)&&(hi->key!=NULL))
                    handleContentFunc(hi->content);
                FREEUP(hi->key);
                if (TRUE==delContent)
                    FREEUP(hi->content);
                hi=temp;
            }
        }
        h->size=0;
        FREEUP(h->hiList);
        FREEUP(h);
        return 1;
    }
    return 0;
}

